package v1

import (
	"context"
	"fmt"

	"quipus/internal/database/dao"
	"quipus/internal/profile"
	v1pb "quipus/proto/gen/api/v1"

	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/adaptor"
	"github.com/gofiber/fiber/v2/middleware/cors"
	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
	"github.com/improbable-eng/grpc-web/go/grpcweb"
	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"

	"quipus/internal/server/router/manager"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/reflection"
)

type APIV1Service struct {
	v1pb.UnimplementedSystemServiceServer
	v1pb.UnimplementedSystemSettingServiceServer
	v1pb.UnimplementedAuthServiceServer
	v1pb.UnimplementedUserServiceServer
	v1pb.UnimplementedKnotServiceServer
	v1pb.UnimplementedResourceServiceServer
	v1pb.UnimplementedInboxServiceServer
	v1pb.UnimplementedActivityServiceServer
	v1pb.UnimplementedWebhookServiceServer
	v1pb.UnimplementedMarkdownServiceServer
	v1pb.UnimplementedIdentityProviderServiceServer
	v1pb.UnimplementedSubjectServiceServer
	v1pb.UnimplementedHelloServiceServer
	v1pb.UnimplementedGranaryServiceServer
	v1pb.UnimplementedLLMServiceServer
	v1pb.UnimplementedTenantServiceServer

	Secret    string
	Profile   *profile.Profile
	Gds       *dao.GdsAccess
	WsManager *manager.WebSocketManager
	// Store   *store.Store

	grpcServer *grpc.Server
}

func NewAPIV1Service(secret string, profile *profile.Profile, grpcServer *grpc.Server, access *dao.GdsAccess) *APIV1Service {
	grpc.EnableTracing = true
	apiv1Service := &APIV1Service{
		Secret:  secret,
		Profile: profile,
		// Store:      store,
		grpcServer: grpcServer,
		Gds:        access,
	}
	v1pb.RegisterSystemServiceServer(grpcServer, apiv1Service)
	v1pb.RegisterSystemSettingServiceServer(grpcServer, apiv1Service)
	v1pb.RegisterAuthServiceServer(grpcServer, apiv1Service)
	v1pb.RegisterUserServiceServer(grpcServer, apiv1Service)
	v1pb.RegisterKnotServiceServer(grpcServer, apiv1Service)
	v1pb.RegisterResourceServiceServer(grpcServer, apiv1Service)
	v1pb.RegisterInboxServiceServer(grpcServer, apiv1Service)
	v1pb.RegisterActivityServiceServer(grpcServer, apiv1Service)
	v1pb.RegisterWebhookServiceServer(grpcServer, apiv1Service)
	v1pb.RegisterMarkdownServiceServer(grpcServer, apiv1Service)
	v1pb.RegisterIdentityProviderServiceServer(grpcServer, apiv1Service)
	v1pb.RegisterSubjectServiceServer(grpcServer, apiv1Service)
	v1pb.RegisterHelloServiceServer(grpcServer, apiv1Service)
	v1pb.RegisterGranaryServiceServer(grpcServer, apiv1Service)
	v1pb.RegisterLLMServiceServer(grpcServer, apiv1Service)
	v1pb.RegisterTenantServiceServer(grpcServer, apiv1Service)

	reflection.Register(grpcServer)
	return apiv1Service
}

// RegisterGateway registers the gRPC-Gateway with the given Echo instance.
func (s *APIV1Service) RegisterGateway(ctx context.Context, fiberServer *fiber.App) error {
	conn, err := grpc.NewClient(
		fmt.Sprintf("%s:%d", s.Profile.Addr, s.Profile.Port),
		grpc.WithTransportCredentials(insecure.NewCredentials()),
		grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(100*1024*1024)),
	)
	if err != nil {
		return err
	}

	gwMux := runtime.NewServeMux()
	if err := v1pb.RegisterSystemServiceHandler(ctx, gwMux, conn); err != nil {
		return err
	}
	if err := v1pb.RegisterSystemSettingServiceHandler(ctx, gwMux, conn); err != nil {
		return err
	}
	if err := v1pb.RegisterAuthServiceHandler(ctx, gwMux, conn); err != nil {
		return err
	}
	if err := v1pb.RegisterUserServiceHandler(ctx, gwMux, conn); err != nil {
		return err
	}
	if err := v1pb.RegisterKnotServiceHandler(ctx, gwMux, conn); err != nil {
		return err
	}
	if err := v1pb.RegisterResourceServiceHandler(ctx, gwMux, conn); err != nil {
		return err
	}
	if err := v1pb.RegisterInboxServiceHandler(ctx, gwMux, conn); err != nil {
		return err
	}
	if err := v1pb.RegisterActivityServiceHandler(ctx, gwMux, conn); err != nil {
		return err
	}
	if err := v1pb.RegisterWebhookServiceHandler(ctx, gwMux, conn); err != nil {
		return err
	}
	if err := v1pb.RegisterMarkdownServiceHandler(ctx, gwMux, conn); err != nil {
		return err
	}
	if err := v1pb.RegisterIdentityProviderServiceHandler(ctx, gwMux, conn); err != nil {
		return err
	}
	if err := v1pb.RegisterSubjectServiceHandler(ctx, gwMux, conn); err != nil {
		return err
	}
	if err := v1pb.RegisterHelloServiceHandler(ctx, gwMux, conn); err != nil {
		return err
	}

	handler := adaptor.HTTPHandler(gwMux)

	// handler := func(c *fiber.Ctx) error {
	// 	gwMux.ServeHTTP(c.Response(), c.Request())
	// 	return nil
	// }

	gwGroup := fiberServer.Group("")
	// gwGroup.Use(cors.New())
	gwGroup.Use(cors.New(cors.Config{
		AllowOrigins: "*",
		AllowMethods: "GET,POST,PUT,DELETE,OPTIONS",
		AllowHeaders: "Origin, Content-Type, Accept",
	}))

	// fiberServer.Use("/api/v1/*", handler)
	// fiberServer.Use("/file/*", handler)
	gwGroup.All("/api/v1/*", handler)
	gwGroup.All("/file/*", handler)

	// GRPC web proxy.
	options := []grpcweb.Option{
		grpcweb.WithCorsForRegisteredEndpointsOnly(false),
		grpcweb.WithOriginFunc(func(string) bool {
			return true
		}),
	}
	// wrappedGrpc := grpcweb.WrapServer(s.grpcServer, options...)
	// grpCHandler := adaptor.HTTPHandler(wrappedGrpc)
	wrappedGrpc := grpcweb.WrapServer(s.grpcServer, options...)
	grpcHandler := adaptor.HTTPHandler(wrappedGrpc)
	// fiberServer.Use("/qps.api.v1.*", grpcHandler)
	fiberServer.All("/qps.api.v1.*", grpcHandler)

	return nil
}

func (s *APIV1Service) RegisterGatewayEcho(ctx context.Context, echoServer *echo.Echo) error {
	conn, err := grpc.NewClient(
		fmt.Sprintf("%s:%d", s.Profile.Addr, s.Profile.Port),
		grpc.WithTransportCredentials(insecure.NewCredentials()),
		grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(100*1024*1024)),
	)
	if err != nil {
		return err
	}

	gwMux := runtime.NewServeMux()
	if err := v1pb.RegisterSystemServiceHandler(ctx, gwMux, conn); err != nil {
		return err
	}
	if err := v1pb.RegisterSystemSettingServiceHandler(ctx, gwMux, conn); err != nil {
		return err
	}
	if err := v1pb.RegisterAuthServiceHandler(ctx, gwMux, conn); err != nil {
		return err
	}
	if err := v1pb.RegisterUserServiceHandler(ctx, gwMux, conn); err != nil {
		return err
	}
	if err := v1pb.RegisterKnotServiceHandler(ctx, gwMux, conn); err != nil {
		return err
	}
	if err := v1pb.RegisterResourceServiceHandler(ctx, gwMux, conn); err != nil {
		return err
	}
	if err := v1pb.RegisterInboxServiceHandler(ctx, gwMux, conn); err != nil {
		return err
	}
	if err := v1pb.RegisterActivityServiceHandler(ctx, gwMux, conn); err != nil {
		return err
	}
	if err := v1pb.RegisterWebhookServiceHandler(ctx, gwMux, conn); err != nil {
		return err
	}
	if err := v1pb.RegisterMarkdownServiceHandler(ctx, gwMux, conn); err != nil {
		return err
	}
	if err := v1pb.RegisterIdentityProviderServiceHandler(ctx, gwMux, conn); err != nil {
		return err
	}
	if err := v1pb.RegisterSubjectServiceHandler(ctx, gwMux, conn); err != nil {
		return err
	}
	if err := v1pb.RegisterHelloServiceHandler(ctx, gwMux, conn); err != nil {
		return err
	}

	gwGroup := echoServer.Group("")
	gwGroup.Use(middleware.CORS())
	handler := echo.WrapHandler(gwMux)

	gwGroup.Any("/api/v1/*", handler)
	gwGroup.Any("/file/*", handler)

	// GRPC web proxy.
	options := []grpcweb.Option{
		grpcweb.WithCorsForRegisteredEndpointsOnly(false),
		grpcweb.WithOriginFunc(func(_ string) bool {
			return true
		}),
	}
	wrappedGrpc := grpcweb.WrapServer(s.grpcServer, options...)
	echoServer.Any("/qps.api.v1.*", echo.WrapHandler(wrappedGrpc))

	return nil
}
